using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Prime31;

#if UNITY_IPHONE || UNITY_ANDROID

public class FacebookBinding
{
	#if UNITY_ANDROID
	private static AndroidJavaObject _facebookPlugin;
	#endif
	
	#if UNITY_IPHONE
	[DllImport("__Internal")]
	private static extern void _facebookInit ();
	
	[DllImport("__Internal")]
	private static extern string _facebookGetAppLaunchUrl ();
	
	[DllImport("__Internal")]
	private static extern void _facebookSetSessionLoginBehavior (int behavior);
	
	[DllImport("__Internal")]
	private static extern void _facebookEnableFrictionlessRequests ();
	
	[DllImport("__Internal")]
	private static extern void _facebookRenewCredentialsForAllFacebookAccounts ();
	
	[DllImport("__Internal")]
	private static extern bool _facebookIsLoggedIn ();
	
	[DllImport("__Internal")]
	private static extern string _facebookGetFacebookAccessToken ();
	
	[DllImport("__Internal")]
	private static extern string _facebookGetSessionPermissions ();
	
	[DllImport("__Internal")]
	private static extern void _facebookLoginUsingDeprecatedAuthorizationFlowWithRequestedPermissions (string perms, string urlSchemeSuffix);
	
	[DllImport("__Internal")]
	private static extern void _facebookLoginWithRequestedPermissions (string perms, string urlSchemeSuffix);
	
	[DllImport("__Internal")]
	private static extern void _facebookReauthorizeWithReadPermissions (string perms);
	
	[DllImport("__Internal")]
	private static extern void _facebookReauthorizeWithPublishPermissions (string perms, int defaultAudience);
	
	[DllImport("__Internal")]
	private static extern void _facebookLogout ();
	
	[DllImport("__Internal")]
	private static extern void _facebookShowDialog (string dialogType, string json);
	
	[DllImport("__Internal")]
	private static extern void _facebookGraphRequest (string graphPath, string httpMethod, string jsonDict);
	
	[DllImport("__Internal")]
	private static extern bool _facebookCanUserUseFacebookComposer ();
	
	[DllImport("__Internal")]
	private static extern void _facebookShowFacebookComposer (string message, string imagePath, string link);
	
	[DllImport("__Internal")]
	private static extern void _facebookShowFacebookShareDialog (string json);
	#endif
	
	static FacebookBinding ()
	{
		#if !UNITY_EDITOR
		#if UNITY_ANDROID
			// find the plugin instance
			using (var pluginClass = new AndroidJavaClass( "com.prime31.FacebookPlugin" ))
				_facebookPlugin = pluginClass.CallStatic<AndroidJavaObject> ("instance");
		#endif
			
			// on login, set the access token
			FacebookManager.preLoginSucceededEvent += () =>
			{
				Facebook.instance.accessToken = getAccessToken ();
		};
		#endif
	}
	
	
	// internal doesn't really do anything here due to this being in the firstpass DLL but it does keep our test harness from touching it
	// you should never need to call this method. The plugin will call it for you when necessary
	internal static void babysitRequest (bool requiresPublishPermissions, Action afterAuthAction)
	{
		#if !UNITY_EDITOR
		new FacebookAuthHelperBin (requiresPublishPermissions, afterAuthAction).start ();
		#endif
	}
	
	
	// Initializes the Facebook plugin for your application
	public static void init ()
	{
		#if !UNITY_EDITOR
		#if UNITY_IPHONE
		_facebookInit ();
		#endif
		
		#if UNITY_ANDROID
		_facebookPlugin.Call ("init", false);
		#endif
		
		// grab the access token in case it is saved
		Facebook.instance.accessToken = getAccessToken ();
		#endif
	}
	
	
	// Gets the url used to launch the application. If no url was used returns string.Empty
	public static string getAppLaunchUrl ()
	{
		#if !UNITY_EDITOR
		#if UNITY_IPHONE
		return _facebookGetAppLaunchUrl ();
		#endif
		
		#if UNITY_ANDROID
		return _facebookPlugin.Call<string> ("getAppLaunchUrl");
		#endif
		
		#endif
		return string.Empty;
	}
	
	
	// Sets the login behavior. Must be called before any login calls! Understand what the login behaviors are and how they work before using this!
	public static void setSessionLoginBehavior (FacebookSessionLoginBehaviorIOS loginBehaviorIOS, FacebookSessionLoginBehaviorAndroid loginBehaviorAndroid)
	{
		#if !UNITY_EDITOR
		#if UNITY_IPHONE
		_facebookSetSessionLoginBehavior ((int)loginBehaviorIOS);
		#endif
		
		#if UNITY_ANDROID
		_facebookPlugin.Call ("setSessionLoginBehavior", loginBehaviorAndroid.ToString ());
		#endif
		#endif

	}
	
	
	// iOS Only. Enables frictionless requests
	public static void enableFrictionlessRequests ()
	{
		#if !UNITY_EDITOR
		#if UNITY_IPHONE
		_facebookEnableFrictionlessRequests ();
		#endif
		#endif
	}
	
	
	// iOS 6 only. Renews the credentials that iOS stores for any native Facebook accounts. You can safely call this at app launch or when logging a user out.
	public static void renewCredentialsForAllFacebookAccounts ()
	{
		#if !UNITY_EDITOR
		#if UNITY_IPHONE
		_facebookRenewCredentialsForAllFacebookAccounts ();
		#endif
		#endif

	}
	
	
	// Checks to see if the current session is valid
	public static bool isSessionValid ()
	{
		#if !UNITY_EDITOR
		#if UNITY_IPHONE
		return _facebookIsLoggedIn ();
		#endif
		
		#if UNITY_ANDROID
		return _facebookPlugin.Call<bool> ("isSessionValid");
		#endif
		
		#endif
		return false;
	}
	
	
	// Gets the current access token
	public static string getAccessToken ()
	{
		#if !UNITY_EDITOR
		#if UNITY_IPHONE
		return _facebookGetFacebookAccessToken ();
		#endif
		
		#if UNITY_ANDROID
		return _facebookPlugin.Call<string> ("getAccessToken");
		#endif
		#endif

		return string.Empty;
	}
	
	
	// Gets the permissions granted to the current access token
	public static List<object> getSessionPermissions ()
	{
		#if !UNITY_EDITOR
		#if UNITY_IPHONE
		var permissions = _facebookGetSessionPermissions ();
		return permissions.listFromJson ();
		#endif
		
		#if UNITY_ANDROID
		var permissions = _facebookPlugin.Call<string> ("getSessionPermissions");
		return permissions.listFromJson ();
		#endif
		
		#endif
		return new List<object> ();
	}
	
	
	//	// Shows the native authorization dialog (iOS 6), opens the Facebook single sign on login in Safari or the official Facebook app with the requested read (not publish!) permissions
	//	[System.Obsolete( "Note that this auth flow has been deprecated by Facebook and could be removed at any time at Facebook's discretion" )]
	//	public static void loginUsingDeprecatedAuthorizationFlowWithRequestedPermissions (string[] permissions)
	//	{
	//		#if UNITY_IPHONE
	//		
	//		#if UNITY_ANDROID
	//		#endif
	//		#pragma warning disable 0618
	//		loginUsingDeprecatedAuthorizationFlowWithRequestedPermissions (permissions, null);
	//		#pragma warning restore 0618
	//	}
	
	
	//	[System.Obsolete( "Note that this auth flow has been deprecated by Facebook and could be removed at any time at Facebook's discretion" )]
	//	public static void loginUsingDeprecatedAuthorizationFlowWithRequestedPermissions (string[] permissions, string urlSchemeSuffix)
	//	{
	//		#if UNITY_IPHONE
	//		#endif
	//		
	//		#if UNITY_ANDROID
	//		#endif
	//		if (Application.platform == RuntimePlatform.IPhonePlayer) {
	//			var permissionsString = string.Join (",", permissions);
	//			_facebookLoginUsingDeprecatedAuthorizationFlowWithRequestedPermissions (permissionsString, urlSchemeSuffix);
	//		}
	//	}
	
	
	// Opens the Facebook single sign on login in Safari, the official Facebook app or uses iOS 6 Accounts if available
	public static void login ()
	{
		loginWithReadPermissions (new string[] {});
	}
	
	
	public static void loginWithReadPermissions (string[] permissions)
	{
		#if !UNITY_EDITOR
		#if UNITY_IPHONE
		loginWithReadPermissions (permissions, null);
		#endif
		
		#if UNITY_ANDROID
		Utils.Debug.Log("Calling facebookAndroidPlugin");
		_facebookPlugin.Call ("loginWithReadPermissions", new object[] { permissions });
		#endif
		#endif
	}
	
	
	// Shows the native authorization dialog (iOS 6), opens the Facebook single sign on login in Safari or the official Facebook app with the requested read (not publish!) permissions
	public static void loginWithReadPermissions (string[] permissions, string urlSchemeSuffix)
	{
		#if !UNITY_EDITOR
		#if UNITY_IPHONE
		string permissionsString = null;
		if (permissions == null || permissions.Length == 0)
			permissionsString = string.Empty;
		else
			permissionsString = string.Join (",", permissions);
		
		_facebookLoginWithRequestedPermissions (permissionsString, urlSchemeSuffix);
		#endif
		#endif
	}
	
	// Android only. Authenticates the user requesting the passed in publish permissions
	public static void loginWithPublishPermissions (string[] permissions)
	{
		#if !UNITY_EDITOR
		#if UNITY_ANDROID
		_facebookPlugin.Call ("loginWithPublishPermissions", new object[] { permissions });
		#endif
		#endif
	}
	
	
	// Reauthorizes with the requested read permissions
	public static void reauthorizeWithReadPermissions (string[] permissions)
	{
		#if !UNITY_EDITOR
		#if UNITY_IPHONE
		var permissionsString = string.Join (",", permissions);
		_facebookReauthorizeWithReadPermissions (permissionsString);
		#endif
		
		#if UNITY_ANDROID
		_facebookPlugin.Call ("reauthorizeWithReadPermissions", permissions.toJson ());
		#endif
		#endif
	}
	
	
	// Reauthorizes with the requested publish permissions and audience
	public static void reauthorizeWithPublishPermissions (string[] permissions, FacebookSessionDefaultAudience defaultAudience)
	{
		#if !UNITY_EDITOR
		#if UNITY_IPHONE
		var permissionsString = string.Join (",", permissions);
		_facebookReauthorizeWithPublishPermissions (permissionsString, (int)defaultAudience);
		#endif
		
		#if UNITY_ANDROID
		// fix up the audience
		string audienceString = null;
		switch (defaultAudience) {
		case FacebookSessionDefaultAudience.OnlyMe:
			audienceString = "ONLY_ME";
			break;
		default:
			audienceString = defaultAudience.ToString ().ToUpper ();
			break;
		}
		
		_facebookPlugin.Call ("reauthorizeWithPublishPermissions", permissions.toJson (), audienceString);
		#endif
		#endif
	}
	
	
	// Logs the user out and invalidates the token
	public static void logout ()
	{
		#if !UNITY_EDITOR
		#if UNITY_IPHONE
		_facebookLogout ();
		#endif
		
		#if UNITY_ANDROID
		_facebookPlugin.Call ("logout");
		#endif
		
		Facebook.instance.accessToken = string.Empty;
		#endif
	}
	
	// Shows the Facebook Share Dialog. Valid dictionary keys (from FBShareDialogParams) are: link, name, caption, description, picture, friends (array)
	public static void showFacebookShareDialog (Dictionary<string,object> parameters)
	{
		#if !UNITY_EDITOR
#if UNITY_IPHONE
		_facebookShowFacebookShareDialog (parameters.toJson ());
		#endif
		
		#if UNITY_ANDROID
		_facebookPlugin.Call ("showFacebookShareDialog", parameters.toJson ());
		#endif
		#endif
	}
	
	
	// Full access to any existing or new Facebook dialogs that get added.  See Facebooks documentation for parameters and dialog types
	public static void showDialog (string dialogType, Dictionary<string,string> parameters)
	{
		#if !UNITY_EDITOR
#if UNITY_IPHONE
		// if we arent logged in start up the babysitter
		if (!isSessionValid ()) {
			babysitRequest (false, () => {
				_facebookShowDialog (dialogType, parameters.toJson ()); });
		} else {
			_facebookShowDialog (dialogType, parameters.toJson ());
		}
		#endif
		
		#if UNITY_ANDROID
		parameters = parameters ?? new Dictionary<string,string> ();
		
		if (!isSessionValid ())
			babysitRequest (false, () => {
				_facebookPlugin.Call ("showDialog", dialogType, parameters.toJson ()); });
		else
			_facebookPlugin.Call ("showDialog", dialogType, parameters.toJson ());
		#endif
		#endif
	}
	
	
	// Allows you to use any available Facebook Graph API method
	public static void graphRequest (string graphPath, string httpMethod, Dictionary<string,string> parameters)
	{
		#if !UNITY_EDITOR
		#if UNITY_IPHONE
		// convert the Dictionary to JSON
		string jsonDict = parameters.toJson ();
		if (jsonDict != null) {
			// if we arent logged in start up the babysitter
			if (!isSessionValid ()) {
				babysitRequest (true, () => {
					_facebookGraphRequest (graphPath, httpMethod, jsonDict); });
			} else {
				_facebookGraphRequest (graphPath, httpMethod, jsonDict);
			}
		}
		#endif
		
		#if UNITY_ANDROID
		parameters = parameters ?? new Dictionary<string,string> ();
		
		// call off to java land
		if (!isSessionValid ())
			babysitRequest (true, () => {
				_facebookPlugin.Call ("graphRequest", graphPath, httpMethod, parameters.toJson ()); });
		else
			_facebookPlugin.Call ("graphRequest", graphPath, httpMethod, parameters.toJson ());
		#endif
		#endif
	}
	
	
	#region iOS6 Facebook composer and Share Dialog
	
	// iOS only. Checks to see if the user is using a version of iOS that supports the Facebook composer and if they have an account setup
	public static bool canUserUseFacebookComposer ()
	{
		#if !UNITY_EDITOR
		#if UNITY_IPHONE
		return _facebookCanUserUseFacebookComposer ();
		#endif
		
		#endif
		return false;
	}
	
	
	// iOS only. 
	public static void showFacebookComposer (string message)
	{
		#if !UNITY_EDITOR
		showFacebookComposer (message, null, null);
		#endif
	}
	
	
	// iOS only. Shows the Facebook composer with optional image path and link
	public static void showFacebookComposer (string message, string imagePath, string link)
	{
		#if !UNITY_EDITOR
		#if UNITY_IPHONE
		_facebookShowFacebookComposer (message, imagePath, link);
		#endif
		#endif
	}

	#endregion
	
}

#region AuthHelper babysitter

public class FacebookAuthHelperBin
{
	public Action afterAuthAction;
	public bool requiresPublishPermissions;
	
	#pragma warning disable
	private static FacebookAuthHelperBin _instance;
	#pragma warning restore
	
	
	public FacebookAuthHelperBin (bool requiresPublishPermissions, Action afterAuthAction)
	{
		_instance = this;
		this.requiresPublishPermissions = requiresPublishPermissions;
		this.afterAuthAction = afterAuthAction;
		
		// login
		FacebookManager.sessionOpenedEvent += sessionOpenedEvent;
		FacebookManager.loginFailedEvent += loginFailedEvent;
		
		// reauth
		if (requiresPublishPermissions) {
			FacebookManager.reauthorizationSucceededEvent -= reauthorizationSucceededEvent;
			FacebookManager.reauthorizationFailedEvent += reauthorizationFailedEvent;
		}
	}
	
	
	~FacebookAuthHelperBin ()
	{
		cleanup ();
	}
	
	
	public void cleanup ()
	{
		// if the afterAuthAction is not null we have not yet cleaned up
		if (afterAuthAction != null) {
			FacebookManager.sessionOpenedEvent -= sessionOpenedEvent;
			FacebookManager.loginFailedEvent -= loginFailedEvent;
			
			if (requiresPublishPermissions) {
				FacebookManager.reauthorizationSucceededEvent -= reauthorizationSucceededEvent;
				FacebookManager.reauthorizationFailedEvent -= reauthorizationFailedEvent;
			}
		}
		
		_instance = null;
	}
	
	
	public void start ()
	{
		FacebookBinding.login ();
	}
	
	
	#region Event handlers
	
	void sessionOpenedEvent ()
	{
		// do we need publish permissions?
		if (requiresPublishPermissions && !FacebookBinding.getSessionPermissions ().Contains ("publish_stream")) {
			
			#if UNITY_ANDROID
			FacebookBinding.reauthorizeWithPublishPermissions (new string[] { "publish_actions", "publish_stream" }, FacebookSessionDefaultAudience.Everyone);
			#endif
			
		} else {
			afterAuthAction ();
			cleanup ();
		}
	}
	
	
	void loginFailedEvent (P31Error error)
	{
		cleanup ();
	}
	
	
	void reauthorizationSucceededEvent ()
	{
		afterAuthAction ();
		cleanup ();
	}
	
	
	void reauthorizationFailedEvent (P31Error error)
	{
		cleanup ();
	}
	
	#endregion
	
}

#endregion

#region Enums
public enum FacebookSessionLoginBehaviorIOS
{
	WithFallbackToWebView,
	WithNoFallbackToWebView,
	ForcingWebView,
	UseSystemAccountIfPresent
}

public enum FacebookSessionDefaultAudience
{
	None,
	OnlyMe,
	Friends,
	Everyone
}
public enum FacebookSessionLoginBehaviorAndroid
{
	SSO_ONLY,
	SSO_WITH_FALLBACK,
	SUPPRESS_SSO
}
#endregion

#endif